import java.io.ObjectStreamException;
import java.io.Serializable;

public class SharedObject implements Serializable, SharedObject_itf {
	
	private static final long serialVersionUID = 5838449924074267286L;
	
	private int id;
	private int lock;
	public Object obj;
	
	public class Lock {
		public final static int NL=0;		// no local lock
		public final static int RLC=1;		// read lock cached (not taken)
		public final static int WLC=2;		// write lock cached
		public final static int RLT=3;		// read lock taken
		public final static int WLT=4;		// write lock taken
		public final static int RLT_WLC=5;	// read lock taken and write lock cached
	}
	
	public SharedObject() {
		super();
		lock = Lock.NL;
	}
	
	// invoked by the user program on the client node
	public void lock_read() {
		synchronized(this) {
			switch(lock) {
			case Lock.WLC :
				lock = Lock.RLT_WLC;
				return;
			case Lock.RLC :
				lock = Lock.RLT;
				return;
			case Lock.NL :
				lock = Lock.RLT;
				break;
			default :
				System.err.println("Lock already taken");
				System.exit(0);
			}
		}
		obj = Client.lock_read(id);
	}

	// invoked by the user program on the client node
	public void lock_write() {
		synchronized(this) {
			switch(lock) {
			case Lock.WLC :
				lock = Lock.WLT;
				return;
			case Lock.RLC :
			case Lock.NL :
				lock = Lock.WLT;
				break;
			default :
				System.err.println("Lock already taken");
				System.exit(0);
			}
		}
		obj = Client.lock_write(id);
	}

	// invoked by the user program on the client node
	public synchronized void unlock() {
		switch(lock) {
		case Lock.RLT :
			lock = Lock.RLC;
			break;
		case Lock.WLT :
			lock = Lock.WLC;
			break;
		case Lock.RLT_WLC :
			lock = Lock.WLC;
			break;
		default:
			System.err.println("Unlocking attempt with no lock taken");
			System.exit(0);
		}
		notify();
	}


	// callback invoked remotely by the server
	public synchronized Object reduce_lock() {
		if(lock==Lock.WLC) lock=Lock.RLC;
		else if(lock==Lock.RLT_WLC) lock=Lock.RLT;
		else if(lock==Lock.WLT) {
			try {
				wait();
				return reduce_lock();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return obj;
	}

	// callback invoked remotely by the server
	public synchronized void invalidate_reader() {
		if(lock==Lock.RLC) lock=Lock.NL;
		else if(lock==Lock.RLT) {
			try {
				wait();
				invalidate_reader();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public synchronized Object invalidate_writer() {
		if(lock==Lock.WLC) lock=Lock.NL;
		else if(lock==Lock.WLT || lock==Lock.RLT_WLC) {
			try {
				wait();
				return invalidate_writer();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return obj;
	}

	public int getId() {
		return id;
	}
	
	public void setId(int _id) {
		id = _id;
	}
}
